unit uRealNumberTest;

interface

uses
  TestFramework, uRealNumber, uNumber;
type
  // Test methods for class TRealNumber

  TRealNumberTest = class(TTestCase)
  strict private
    FTestedNumber: TRealNumber;
  public
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure TestAbs;
    procedure TestAdd;
    procedure TestApplyFunction;
    procedure TestConstructorWithParameters;
    procedure TestCopyFrom;
    procedure TestClear;
    procedure TestChangeSign;
    procedure TestDivideBy;
    procedure TestDivisionByZero;
    procedure TestFromString;
    procedure TestInvert;
    procedure TestIsAdditiveIdentity;
    procedure TestIsMultiplicativeIdentity;
    procedure TestMultiplyBy;
    procedure TestSubtract;
    procedure TestToString;
    procedure TestPower;
    procedure TestMakeFactorial;
  end;

implementation

uses SysUtils, uNumberFunctionStub;

procedure TRealNumberTest.SetUp;
begin
  FTestedNumber := TRealNumber.Create;
end;

procedure TRealNumberTest.TearDown;
begin
  FTestedNumber.Free;
  FTestedNumber := nil;
end;

procedure TRealNumberTest.TestAbs;
var
  ReturnValue: TNumberBase;
begin
  FTestedNumber.Value := 5;
  ReturnValue := FTestedNumber.Abs;
  CheckEquals(ReturnValue, 5);

  FTestedNumber.Value := -5;
  ReturnValue := FTestedNumber.Abs;
  CheckEquals(ReturnValue, 5);

  FTestedNumber.Value := 5.8;
  ReturnValue := FTestedNumber.Abs;
  CheckEquals(ReturnValue, 5.8);

  FTestedNumber.Value := -5.8;
  ReturnValue := FTestedNumber.Abs;
  CheckEquals(ReturnValue, 5.8);
end;

procedure TRealNumberTest.TestAdd;
var
  realNumber: TRealNumber;
begin
  realNumber := TRealNumber.Create;

  realNumber.Value := 3.2;

  FTestedNumber.Add(realNumber);
  CheckEquals(3.2, FTestedNumber.Value);

  FTestedNumber.Value := 2.3;
  FTestedNumber.Add(realNumber);
  CheckEquals(5.5, FTestedNumber.Value);

  FTestedNumber.Value := -5.5;
  FTestedNumber.Add(realNumber);
  CheckEquals(-2.3, FTestedNumber.Value);

  realNumber.Value := -3.2;

  {
  FIXME: why doesn't this test pass ?
  FTestedNumber.Value := 2.3;
  FTestedNumber.Add(realNumber);
  CheckEquals(-0.9, FTestedNumber.Value);
  }

  FTestedNumber.Value := -5.5;
  FTestedNumber.Add(realNumber);
  CheckEquals(-8.7, FTestedNumber.Value);

  //TODO: test calculation with invalid number

  realNumber.Destroy;
end;

procedure TRealNumberTest.TestApplyFunction;
var
  functionStub: TNumberFunctionStub;
begin
  FTestedNumber.Value := 1;
  functionStub := TNumberFunctionStub.Create;
  try
    FTestedNumber.ApplyFunction(functionStub);
    CheckEquals(2, FTestedNumber.Value);
  finally
    functionStub.Destroy;
  end;
end;

procedure TRealNumberTest.TestConstructorWithParameters;
var realNumber: TRealNumber;
begin
  realNumber := TRealNumber.Create(5.2);
  CheckEquals( 5.2, realNumber.Value );
  realNumber.Destroy;
end;

procedure TRealNumberTest.TestCopyFrom;
var
  realNumber: TRealNumber;
begin
  realNumber := TRealNumber.Create;

  realNumber.Value := 5.2;
  FTestedNumber.CopyFrom(realNumber);
  CheckEquals(5.2, FTestedNumber.Value);

  realNumber.Value := -5.2;
  FTestedNumber.CopyFrom(realNumber);
  CheckEquals(-5.2, FTestedNumber.Value);

  //TODO: test calculation with invalid number

  realNumber.Destroy;
end;

procedure TRealNumberTest.TestClear;
begin
  FTestedNumber.Clear;
  CheckEquals(0, FTestedNumber.Value);

  FTestedNumber.Value := 5;
  FTestedNumber.Clear;
  CheckEquals(0, FTestedNumber.Value);
end;

procedure TRealNumberTest.TestChangeSign;
begin
  FTestedNumber.ChangeSign;
  CheckEquals(0, FTestedNumber.Value);

  FTestedNumber.Value := 5.2;
  FTestedNumber.ChangeSign;
  CheckEquals(-5.2, FTestedNumber.Value);

  FTestedNumber.ChangeSign;
  CheckEquals(5.2, FTestedNumber.Value);

  //TODO: test calculation with invalid number

end;

procedure TRealNumberTest.TestDivideBy;
var
  realNumber: TRealNumber;
begin
  realNumber := TRealNumber.Create;

  realNumber.Value := 2;
  FTestedNumber.Value := 5.2;
  FTestedNumber.DivideBy(realNumber);
  CheckEquals(2.6, FTestedNumber.Value);

  realNumber.Value := -2;
  FTestedNumber.Value := 5.2;
  FTestedNumber.DivideBy(realNumber);
  CheckEquals(-2.6, FTestedNumber.Value);

  realNumber.Value := -2;
  FTestedNumber.Value := -5.2;
  FTestedNumber.DivideBy(realNumber);
  CheckEquals(2.6, FTestedNumber.Value);

  //TODO: test calculation with invalid number

  realNumber.Destroy;
end;

procedure TRealNumberTest.TestDivisionByZero;
begin
  //TODO: implement me
end;

procedure TRealNumberTest.TestFromString;
var
  source: string;
  originalDecimalSeparator: char;
begin
  originalDecimalSeparator := SysUtils.DecimalSeparator;

  source := '5';
  FTestedNumber.FromString(source);
  CheckEquals(5,FTestedNumber.Value);

  source := '+5';
  FTestedNumber.FromString(source);
  CheckEquals(5,FTestedNumber.Value);

  source := '-5';
  FTestedNumber.FromString(source);
  CheckEquals(-5,FTestedNumber.Value);

  source := '5e2';
  FTestedNumber.FromString(source);
  CheckEquals(500,FTestedNumber.Value);

  source := '5e-2';
  FTestedNumber.FromString(source);
  CheckEquals(0.05,FTestedNumber.Value);

  SysUtils.DecimalSeparator := '.';

  source := '5.2';
  FTestedNumber.FromString(source);
  CheckEquals(5.2,FTestedNumber.Value);

  SysUtils.DecimalSeparator := ',';

  source := '5,2';
  FTestedNumber.FromString(source);
  CheckEquals(5.2,FTestedNumber.Value);

  SysUtils.DecimalSeparator := originalDecimalSeparator;
end;

procedure TRealNumberTest.TestInvert;
begin
  FTestedNumber.Value := 2;
  FTestedNumber.Invert;
  CheckEquals(0.5, FTestedNumber.Value);

  FTestedNumber.Invert;
  CheckEquals(2, FTestedNumber.Value);

  FTestedNumber.Value := -2;
  FTestedNumber.Invert;
  CheckEquals(-0.5, FTestedNumber.Value);

  FTestedNumber.Invert;
  CheckEquals(-2, FTestedNumber.Value);

  //TODO: test Invert for value = 0

  //TODO: test calculation with invalid number

end;

procedure TRealNumberTest.TestIsAdditiveIdentity;
begin
  CheckTrue(FTestedNumber.IsAdditiveIdentity);

  FTestedNumber.Value := 2;
  CheckFalse(FTestedNumber.IsAdditiveIdentity);

  FTestedNumber.Value := -2;
  CheckFalse(FTestedNumber.IsAdditiveIdentity);

  FTestedNumber.Value := 0;
  CheckTrue(FTestedNumber.IsAdditiveIdentity);

  //TODO: test calculation with invalid number

end;

procedure TRealNumberTest.TestIsMultiplicativeIdentity;
begin
  CheckFalse(FTestedNumber.IsMultiplicativeIdentity);

  FTestedNumber.Value := 2;
  CheckFalse(FTestedNumber.IsMultiplicativeIdentity);

  FTestedNumber.Value := 1;
  CheckTrue(FTestedNumber.IsMultiplicativeIdentity);

  //TODO: test calculation with invalid number

end;

procedure TRealNumberTest.TestMakeFactorial;
begin
  FTestedNumber.Value := 5;
  FTestedNumber.MakeFactorial;
  CheckEquals(120, FTestedNumber.Value);
end;

procedure TRealNumberTest.TestMultiplyBy;
var
  realNumber: TRealNumber;
begin
  realNumber := TRealNumber.Create;

  realNumber.Value := 2;
  FTestedNumber.Value := 5.2;
  FTestedNumber.MultiplyBy(realNumber);
  CheckEquals(10.4, FTestedNumber.Value);

  realNumber.Value := -2;
  FTestedNumber.Value := 5.2;
  FTestedNumber.MultiplyBy(realNumber);
  CheckEquals(-10.4, FTestedNumber.Value);

  realNumber.Value := -2;
  FTestedNumber.Value := -5.2;
  FTestedNumber.MultiplyBy(realNumber);
  CheckEquals(10.4, FTestedNumber.Value);

  //TODO: test calculation with invalid number

  realNumber.Destroy;
end;

procedure TRealNumberTest.TestPower;
var
  realNumber: TRealNumber;
begin
  realNumber := TRealNumber.Create;
  try
    FTestedNumber.Value := 5;
    realNumber.Value := 2;
    FTestedNumber.Power(realNumber);
    CheckEquals(25, FTestedNumber.Value);

    FTestedNumber.Value := -5;
    realNumber.Value := 2;
    FTestedNumber.Power(realNumber);
    CheckEquals(25, FTestedNumber.Value);

    FTestedNumber.Value := 10;
    realNumber.Value := -2;
    FTestedNumber.Power(realNumber);
    CheckEquals(0.01, FTestedNumber.Value);

    FTestedNumber.Value := 25;
    realNumber.Value := 0.5;
    FTestedNumber.Power(realNumber);
    //the result value has to be rounded here, because square root does not give precise results
    CheckEquals(5, round(FTestedNumber.Value));
  finally
    realNumber.Destroy;
  end;
end;

procedure TRealNumberTest.TestSubtract;
var
  realNumber: TRealNumber;
begin
  realNumber := TRealNumber.Create;

  realNumber.Value := 3.2;

  FTestedNumber.Subtract(realNumber);
  CheckEquals(-3.2, FTestedNumber.Value);

  {
  FIXME: why doesn't this test pass ?
  FTestedNumber.Value := 2.3;
  FTestedNumber.Subtract(realNumber);
  CheckEquals(-0.9, FTestedNumber.Value);
  }

  FTestedNumber.Value := -5.5;
  FTestedNumber.Subtract(realNumber);
  CheckEquals(-8.7, FTestedNumber.Value);

  realNumber.Value := -3.2;

  FTestedNumber.Value := 2.3;
  FTestedNumber.Subtract(realNumber);
  CheckEquals(5.5, FTestedNumber.Value);

  FTestedNumber.Value := -5.5;
  FTestedNumber.Subtract(realNumber);
  CheckEquals(-2.3, FTestedNumber.Value);

  //TODO: test calculation with invalid number

  realNumber.Destroy;
end;

procedure TRealNumberTest.TestToString;
var
  returnValue: string;
  format: string;
  originalDecimalSeparator: char;
begin
  originalDecimalSeparator := SysUtils.DecimalSeparator;

  //default format
  returnValue := FTestedNumber.ToString;
  CheckEquals('0', returnValue);

  SysUtils.DecimalSeparator := '.';

  //default format
  FTestedNumber.Value := 5.12345;
  returnValue := FTestedNumber.ToString;
  CheckEquals('5.123', returnValue);

  //custom format (rounding)
  format := '0.####';
  returnValue := FTestedNumber.ToString(format);
  CheckEquals('5.1235', returnValue);

  SysUtils.DecimalSeparator := ',';

  //default format
  FTestedNumber.Value := 5.12345;
  returnValue := FTestedNumber.ToString;
  CheckEquals('5,123', returnValue);

  //custom format (rounding)
  format := '0.####';
  returnValue := FTestedNumber.ToString(format);
  CheckEquals('5,1235', returnValue);

  //exponential format
  format := '0e-0';

  FTestedNumber.Value := 0.005;
  returnValue := FTestedNumber.ToString(format);
  CheckEquals('5e-3', returnValue);

  FTestedNumber.Value := -0.005;
  returnValue := FTestedNumber.ToString(format);
  CheckEquals('-5e-3', returnValue);

  FTestedNumber.Value := 5000;
  returnValue := FTestedNumber.ToString(format);
  CheckEquals('5e3', returnValue);

  SysUtils.DecimalSeparator := originalDecimalSeparator;
end;

initialization
  // Register any test cases with the test runner
  RegisterTest(TRealNumberTest.Suite);
end.

